using System;
using System.Collections.Generic;
using System.Text;

namespace LumiSoft.Net.RTP
{
    /// <summary>
    /// This class implements RTCP SDES packet 1 "chunk". 
    /// </summary>
    public class RTCP_Packet_SDES_Chunk
    {
        private uint   m_Source   = 0;
        private string m_CName    = null;
        private string m_Name     = null;
        private string m_Email    = null;
        private string m_Phone    = null;
        private string m_Location = null;
        private string m_Tool     = null;
        private string m_Note     = null;

        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="source">SSRC or CSRC identifier.</param>
        /// <param name="cname">Canonical End-Point Identifier.</param>
        /// <exception cref="ArgumentException">Is raised when invalid value is passed.</exception>
        public RTCP_Packet_SDES_Chunk(uint source,string cname)
        {
            if(source == 0){
                throw new ArgumentException("Argument 'source' value must be > 0.");
            }
            if(string.IsNullOrEmpty(cname)){
                throw new ArgumentException("Argument 'cname' value may not be null or empty.");
            }

            m_Source = source;
            m_CName  = cname;
        }

        /// <summary>
        /// Parser constructor.
        /// </summary>
        internal RTCP_Packet_SDES_Chunk()
        {
        }


        #region method Parse

        /// <summary>
        /// Parses SDES chunk from the specified buffer.
        /// </summary>
        /// <param name="buffer">Buffer which contains SDES chunk.</param>
        /// <param name="offset">Offset in buffer.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public void Parse(byte[] buffer,ref int offset)
        {
            if(buffer == null){
                throw new ArgumentNullException("buffer");
            }
            if(offset < 0){
                throw new ArgumentException("Argument 'offset' value must be >= 0.");
            }
                        
            // Read SSRC/CSRC
            m_Source = (uint)(buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]);

            // Read SDES items while reach end of buffer or we get chunk terminator(\0).
            while(offset < buffer.Length && buffer[offset] != 0){
                int type   = buffer[offset++];
                int length = buffer[offset++];

                // CNAME
                if(type == 1){
                    m_CName = Encoding.UTF8.GetString(buffer,offset,length);
                }
                // NAME
                else if(type == 2){
                    m_Name = Encoding.UTF8.GetString(buffer,offset,length);
                }
                // EMAIL
                else if(type == 3){
                    m_Email = Encoding.UTF8.GetString(buffer,offset,length);
                }
                // PHONE
                else if(type == 4){
                    m_Phone = Encoding.UTF8.GetString(buffer,offset,length);
                }
                // LOC
                else if(type == 5){
                    m_Location = Encoding.UTF8.GetString(buffer,offset,length);
                }
                // TOOL
                else if(type == 6){
                    m_Tool = Encoding.UTF8.GetString(buffer,offset,length);
                }
                // NOTE
                else if(type == 7){
                    m_Note = Encoding.UTF8.GetString(buffer,offset,length);
                }
                // PRIV
                else if(type == 8){
                    // TODO:
                }
                offset += length;
            }
        }

        #endregion

        #region method ToByte

        /// <summary>
        /// Stores SDES junk to the specified buffer.
        /// </summary>
        /// <param name="buffer">Buffer where to store data.</param>
        /// <param name="offset">Offset in buffer.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public void ToByte(byte[] buffer,ref int offset)
        {
            if(buffer == null){
                throw new ArgumentNullException("buffer");
            }
            if(offset < 0){
                throw new ArgumentException("Argument 'offset' value must be >= 0.");
            }

            int startOffset = offset;

            // SSRC/SDES
            buffer[offset++] = (byte)((m_Source >> 24) | 0xFF);
            buffer[offset++] = (byte)((m_Source >> 16) | 0xFF);
            buffer[offset++] = (byte)((m_Source >>  8) | 0xFF);
            buffer[offset++] = (byte)((m_Source)       | 0xFF);

            //--- SDES items -----------------------------------
            if(!string.IsNullOrEmpty(m_CName)){
                byte[] b = Encoding.UTF8.GetBytes(m_CName);
                buffer[offset++] = 1;
                buffer[offset++] = (byte)b.Length;
                Array.Copy(b,0,buffer,offset,b.Length);
                offset += b.Length;
            }
            if(!string.IsNullOrEmpty(m_Name)){
                byte[] b = Encoding.UTF8.GetBytes(m_Name);
                buffer[offset++] = 2;
                buffer[offset++] = (byte)b.Length;
                Array.Copy(b,0,buffer,offset,b.Length);
                offset += b.Length;
            }
            if(!string.IsNullOrEmpty(m_Email)){
                byte[] b = Encoding.UTF8.GetBytes(m_Email);
                buffer[offset++] = 3;
                buffer[offset++] = (byte)b.Length;
                Array.Copy(b,0,buffer,offset,b.Length);
                offset += b.Length;
            }
            if(!string.IsNullOrEmpty(m_Phone)){
                byte[] b = Encoding.UTF8.GetBytes(m_Phone);
                buffer[offset++] = 4;
                buffer[offset++] = (byte)b.Length;
                Array.Copy(b,0,buffer,offset,b.Length);
                offset += b.Length;
            }
            if(!string.IsNullOrEmpty(m_Location)){
                byte[] b = Encoding.UTF8.GetBytes(m_Location);
                buffer[offset++] = 5;
                buffer[offset++] = (byte)b.Length;
                Array.Copy(b,0,buffer,offset,b.Length);
                offset += b.Length;
            }
            if(!string.IsNullOrEmpty(m_Tool)){
                byte[] b = Encoding.UTF8.GetBytes(m_Tool);
                buffer[offset++] = 6;
                buffer[offset++] = (byte)b.Length;
                Array.Copy(b,0,buffer,offset,b.Length);
                offset += b.Length;
            }
            if(!string.IsNullOrEmpty(m_Note)){
                byte[] b = Encoding.UTF8.GetBytes(m_Tool);
                buffer[offset++] = 7;
                buffer[offset++] = (byte)b.Length;
                Array.Copy(b,0,buffer,offset,b.Length);
                offset += b.Length;
            }
            // Terminate chunk
            buffer[offset++] = 0;

            // Pad 4 bytes boundary
            for(int i=0;i<((offset - startOffset) % 4);i++){
                buffer[offset++] = 0;
            }
        }

        #endregion


        #region method Set

        /// <summary>
        /// Gets all data from specified value and sets all values.
        /// </summary>
        /// <param name="description">Source description.</param>
        internal void Set(RTP_SourceDescription description)
        {
            m_Name     = description.Name;
            m_Email    = description.Email;
            m_Phone    = description.Phone;
            m_Location = description.Location;
            m_Tool     = description.Tool;
            m_Note     = description.Note;
        }

        #endregion


        #region Properties Implementation

        /// <summary>
        /// Gets SSRC or CSRC identifier.
        /// </summary>
        public uint Source
        {
            get{ return m_Source; }
        }

        /// <summary>
        /// Gets Canonical End-Point Identifier.
        /// </summary>
        public string CName
        {
            get{ return m_CName; }
        }

        /// <summary>
        /// Gets or sets the real name, eg. "John Doe". Value null means not specified.
        /// </summary>
        /// <exception cref="ArgumentException">Is raised when invalid value is passed.</exception>
        public string Name
        {
            get{ return m_Name; }

            set{
                if(Encoding.UTF8.GetByteCount(value) > 255){
                    throw new ArgumentException("Property 'Name' value must be <= 255 bytes.");
                }

                m_Name = value;
            }
        }

        /// <summary>
        /// Gets or sets email address. For example "John.Doe@example.com". Value null means not specified.
        /// </summary>
        /// <exception cref="ArgumentException">Is raised when invalid value is passed.</exception>
        public string Email
        {
            get{ return m_Email; }

            set{
                if(Encoding.UTF8.GetByteCount(value) > 255){
                    throw new ArgumentException("Property 'Email' value must be <= 255 bytes.");
                }

                m_Email = value;
            }
        }

        /// <summary>
        /// Gets or sets phone number. For example "+1 908 555 1212". Value null means not specified.
        /// </summary>
        /// <exception cref="ArgumentException">Is raised when invalid value is passed.</exception>
        public string Phone
        {
            get{ return m_Phone; }

            set{
                if(Encoding.UTF8.GetByteCount(value) > 255){
                    throw new ArgumentException("Property 'Phone' value must be <= 255 bytes.");
                }

                m_Phone = value;
            }
        }

        /// <summary>
        /// Gets gets location string. It may be geographic address or for example chat room name.
        /// Value null means not specified.
        /// </summary>
        /// <exception cref="ArgumentException">Is raised when invalid value is passed.</exception>
        public string Location
        {
            get{ return m_Location; }

            set{
                if(Encoding.UTF8.GetByteCount(value) > 255){
                    throw new ArgumentException("Property 'Location' value must be <= 255 bytes.");
                }

                m_Location = value;
            }
        }

        /// <summary>
        /// Gets or sets streaming application name/version.
        /// Value null means not specified.
        /// </summary>
        /// <exception cref="ArgumentException">Is raised when invalid value is passed.</exception>
        public string Tool
        {
            get{ return m_Tool; }

            set{
                if(Encoding.UTF8.GetByteCount(value) > 255){
                    throw new ArgumentException("Property 'Tool' value must be <= 255 bytes.");
                }

                m_Tool = value;
            }
        }

        /// <summary>
        /// Gets or sets note text. The NOTE item is intended for transient messages describing the current state
        /// of the source, e.g., "on the phone, can't talk".
        /// Value null means not specified.
        /// </summary>
        /// <exception cref="ArgumentException">Is raised when invalid value is passed.</exception>
        public string Note
        {
            get{ return m_Note; }

            set{
                if(Encoding.UTF8.GetByteCount(value) > 255){
                    throw new ArgumentException("Property 'Note' value must be <= 255 bytes.");
                }

                m_Note = value;
            }
        }

        /// <summary>
        /// Gets number of bytes needed for this SDES chunk.
        /// </summary>
        public int Size
        {
            get{
                int size = 4;
                if(m_CName != null){
                    size += Encoding.UTF8.GetByteCount(m_CName);
                }
                if(m_Name != null){
                    size += Encoding.UTF8.GetByteCount(m_Name);
                }
                if(m_Email != null){
                    size += Encoding.UTF8.GetByteCount(m_Email);
                }
                if(m_Phone != null){
                    size += Encoding.UTF8.GetByteCount(m_Phone);
                }
                if(m_Location != null){
                    size += Encoding.UTF8.GetByteCount(m_Location);
                }
                if(m_Tool != null){
                    size += Encoding.UTF8.GetByteCount(m_Tool);
                }
                if(m_Note != null){
                    size += Encoding.UTF8.GetByteCount(m_Note);
                }

                return size; 
            }
        }

        #endregion

    }
}
